﻿#include "stdafx.h"
#if _QG_USE_ES2
#include "qg_es2_dev.h"
#include "qg_es2_mtrl.h"
#include "qg_es2_cnv.h"
#include "qg_trace.h"

// 재질 바인드
bool ES2Device::_BindMtrl(ksize_t maxtexcount) throw()
{
	if (_sprn->bd_rasz)
	{
		_sprn->bd_rasz = false;
		_CommitRasterize(((ES2Rasz*)_sprn->m_rasz)->_data, _vprn.m_rasz_data);
	}

	if (_sprn->bd_blnd)
	{
		_sprn->bd_blnd = false;
		_CommitBlendState(((ES2Blnd*)_sprn->m_blnd)->_data, _vprn.m_blnd_data);
	}

	for (ksize_t i = 0; i < maxtexcount; i++)
	{
		if (!_vprn.bd_tex[i])
			continue;

		_vprn.bd_tex[i] = false;

		if (_CommitTexture((kint)i, _sprn->m_tex[i]) && _sprn->m_splr[i])
		{
			const qgSplrData& nsp = ((ES2Splr*)_sprn->m_splr[i])->_data;
			qgSplrData& psp = ((ES2Texture*)_sprn->m_tex[i])->_splr;

			// 몇개 안쓴다 고로 걍 보내자
			// : filt, addru, addrv
			_CommitSampler((kint)i, nsp, psp);
		}
	}

	return true;
}

// 실제 털났나 비교
void ES2Device::_CommitRasterize(const qgRaszData& nrz, qgRaszData& prz) throw()
{
	// 깊이 바이어스, 내부에서 채우기 정보를 확인하므로 채우기 값을 변경하기 전에 처리해야한다
	if (nrz.dbias != prz.dbias || !k_eq(nrz.slope_scale, prz.slope_scale))
	{
		prz.dbias = nrz.dbias;
		prz.slope_scale = nrz.slope_scale;

		if (nrz.dbias != 0 || nrz.slope_scale != 0.0f)
		{
			// QGFILL_SOLID 밖에 지원하지 않음
			glEnable(GL_POLYGON_OFFSET_FILL);
			glPolygonOffset(nrz.slope_scale, (GLfloat)nrz.dbias);
		}
		else
		{
			// 이전 상태값을 보고 처리... 이대로 안전하겠지
			glDisable(GL_POLYGON_OFFSET_FILL);
		}
	}

	// 채우기
	if (nrz.fill != prz.fill)
	{
		prz.fill = nrz.fill;

		//glPolygonMode() 지원한함
	}

	// 컬링
	if (nrz.cull != prz.cull)
	{
		switch (nrz.cull)
		{
			case QGCULL_NONE:
				prz.cull = QGCULL_NONE;

				glDisable(GL_CULL_FACE);
				break;

			case QGCULL_FRONT:
				if (prz.cull == QGCULL_NONE)
					glEnable(GL_CULL_FACE);

				prz.cull = QGCULL_FRONT;

				glCullFace(GL_FRONT);
				break;

			case QGCULL_BACK:
				if (prz.cull == QGCULL_NONE)
					glEnable(GL_CULL_FACE);

				prz.cull = QGCULL_BACK;

				glCullFace(GL_BACK);
				break;
		}
	}

	// 깊이 쓰기
	if (nrz.dwrite != prz.dwrite)
	{
		prz.dwrite = nrz.dwrite;

		glDepthMask(nrz.dwrite ? GL_TRUE : GL_FALSE);
	}

	// 깊이 함수
	if (nrz.dfunc != prz.dfunc)
	{
		GLint n = ES2Convert::ComparisonOp(nrz.dfunc);

		if (n == 0)
		{
			prz.dfunc = QGCOP_NONE;

			glDisable(GL_DEPTH_TEST);
		}
		else
		{
			if (prz.dfunc == QGCOP_NONE)
				glEnable(GL_DEPTH_TEST);

			prz.dfunc = nrz.dfunc;

			glDepthFunc(n);
		}
	}

	// 깊이 자르기(depth_clamp)
	// ARB, NV에만 있더라... 귀찮으니 패스
}

// 블렌디 한잔!!
void ES2Device::_CommitBlendState(const qgBlndData& nbd, qgBlndData& pbd) throw()
{
	// 블렌드 연산자
	GLenum blop = ES2Convert::BlendOp(nbd.blend.op);

	// 색깔 쓰기 마스크
	if (nbd.mask != pbd.mask)
	{
		pbd.mask = nbd.mask;

		GLboolean r = nbd.mask & QGMASK_RED ? GL_TRUE : GL_FALSE;
		GLboolean g = nbd.mask & QGMASK_GREEN ? GL_TRUE : GL_FALSE;
		GLboolean b = nbd.mask & QGMASK_BLUE ? GL_TRUE : GL_FALSE;
		GLboolean a = nbd.mask & QGMASK_ALPHA ? GL_TRUE : GL_FALSE;
		glColorMask(r, g, b, a);
	}

	// 블렌드
	if (blop == 0)
	{
		// 블렌딩 끔
		if (_vprn.m_blnd_blend)
		{
			_vprn.m_blnd_blend = false;

			glDisable(GL_BLEND);
		}

		// 알파도 끄고, 다른 설정은 안해도 됨
		_vprn.m_blnd_alpha = false;

		pbd.blend.op = QGBLO_NONE;
		pbd.blend.src = QGBLV_NONE;
		pbd.blend.dest = QGBLV_NONE;
		pbd.alpha.op = QGBLO_NONE;
		pbd.alpha.src = QGBLV_NONE;
		pbd.alpha.dest = QGBLV_NONE;
	}
	else
	{
		GLenum alop = ES2Convert::BlendOp(nbd.alpha.op);

		if (!_vprn.m_blnd_blend)
		{
			_vprn.m_blnd_blend = true;

			glEnable(GL_BLEND);
		}

		if (alop == 0)
		{
			// RGB만 사용
			if (nbd.blend.op != pbd.blend.op || nbd.blend.src != pbd.blend.src || nbd.blend.dest != pbd.blend.dest)
			{
				pbd.blend = nbd.blend;
				pbd.alpha.op = QGBLO_NONE;
				pbd.alpha.src = QGBLV_NONE;
				pbd.alpha.dest = QGBLV_NONE;

				glBlendEquation(blop);

				glBlendFuncSeparate(
					ES2Convert::BlendValue(nbd.blend.src),
					ES2Convert::BlendValue(nbd.blend.dest),
					GL_ONE, GL_ZERO);
			}

			// 알파 설정은 끔
			_vprn.m_blnd_alpha = false;
		}
		else
		{
			// RGB, ALPHA 둘다 사용
			if (nbd.blend.op != pbd.blend.op || nbd.blend.src != pbd.blend.src || nbd.blend.dest != pbd.blend.dest ||
				nbd.alpha.op != pbd.alpha.op || nbd.alpha.src != pbd.alpha.src || nbd.alpha.dest != pbd.alpha.dest)
			{
				pbd.blend = nbd.blend;
				pbd.alpha = nbd.alpha;

				glBlendEquation(blop);

				glBlendFuncSeparate(
					ES2Convert::BlendValue(nbd.blend.src),
					ES2Convert::BlendValue(nbd.blend.dest),
					ES2Convert::BlendValue(nbd.alpha.src),
					ES2Convert::BlendValue(nbd.alpha.dest));
			}

			// 알파 설정 켬
			_vprn.m_blnd_alpha = true;
		}
	}
}

// 샘플라
void ES2Device::_CommitSampler(kint stage, const qgSplrData& nsp, qgSplrData& psp) throw()
{
	// 텍스쳐 설정 안해도됨 반드시 _CommitTexture()를 먼저 실행하기 때문에

	// 필터
	if (nsp.filt != psp.filt)
	{
		psp.filt = nsp.filt;

		GLint min, mag;
		ES2Convert::Filter(nsp.filt, _sprn->m_tex[stage]->HasMips(), &mag, &min);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min);

		// 비등방성 필터 지원 안함
	}

	// 가로 주소 처리 (U=S=가로)
	if (nsp.addru != psp.addru)
	{
		psp.addru = nsp.addru;

		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, ES2Convert::Address(nsp.addru));
	}

	// 세로 주소 처리 (V=T=세로)
	if (nsp.addrv != psp.addrv)
	{
		psp.addrv = nsp.addrv;

		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, ES2Convert::Address(nsp.addrv));
	}

	// 깊이 주소 처리 (W=R=깊이)
	if (nsp.addrw != psp.addrw)
	{
		psp.addrw = nsp.addrw;

		// 지원 안함. ES3에서 지원함.
		//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, ES2Convert::Address(nsp.addrw));
	}

	// cmpop
	// 지원 안함.

	// 둘레
	// 지원 안함.

	// LOD 바이어스
	// 지원 안함.
}

//
qgRasz* ES2Device::_RoamImplRasz(const qgRaszData& data)
{
	ES2Rasz* rasz = kobj_create(ES2Rasz);

	rasz->_data = data;

	return rasz;
}

//
qgBlnd* ES2Device::_RoamImplBlnd(const qgBlndData& data)
{
	ES2Blnd* blnd = kobj_create(ES2Blnd);

	blnd->_data = data;

	return blnd;
}

//
qgSplr* ES2Device::_RoamImplSplr(const qgSplrData& data)
{
	ES2Splr* splr = kobj_create(ES2Splr);

	splr->_data = data;

	return splr;
}


//////////////////////////////////////////////////////////////////////////
// 래스터라이저
KIMPL_SUBCLASS(ES2Rasz, "ES2Rasz");


//////////////////////////////////////////////////////////////////////////
// 블랜드
KIMPL_SUBCLASS(ES2Blnd, "ES2Blnd");


//////////////////////////////////////////////////////////////////////////
// 샘플러
KIMPL_SUBCLASS(ES2Splr, "ES2Splr");

#endif	// _QG_USE_ES2
